#include "cJSON.h"
#include "esp_event.h"
#include "esp_https_server.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "esp_vfs_semihost.h"
#include "esp_wifi.h"
#include "lwip/err.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include <esp_event.h>
#include <esp_log.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <nvs_flash.h>
#include <sys/param.h>


#include "cJSON.h"
#include "driver/gpio.h"
#include "esp_netif.h"


#include "ArnoldWeb.h"
#include <esp_http_server.h>
#include <string.h>

#ifndef MIN
#    define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
esp_err_t          start_webserver(const char* base_path);
void               CJON_Test(httpd_req_t* req);
esp_err_t          post_handler(httpd_req_t* req);
esp_err_t          http_request_handler(httpd_req_t* req);
static const char* TAG = "https server";

static char buf[100] = "{\"status\": true}";
// HTTP GET 请求回调处理函数
static esp_err_t esp_light_get_handler(httpd_req_t* req)
{
    // 发送含有灯的状态的 JSON 格式数据给客户端
    httpd_resp_send(req, buf, strlen(buf));
    printf("esp_light_get_handler\r\n");
    return ESP_OK;
}
// HTTP POST 请求回调处理函数
static esp_err_t esp_light_set_handler(httpd_req_t* req)
{
    int ret, remaining = req->content_len;
    memset(buf, 0, sizeof(buf));
    printf("esp_light_set_handler\r\n");

    http_request_handler(req);

    return ESP_OK;
}

// Get 对应的处理回调函数
static const httpd_uri_t status = {
    .uri     = "/bobo",
    .method  = HTTP_GET,
    .handler = esp_light_get_handler,
};

// Post 对应的处理回调函数
static const httpd_uri_t ctrl = {
    .uri     = "/bobo",
    .method  = HTTP_POST,
    .handler = esp_light_set_handler,
};

esp_err_t esp_start_webserver()
{
    httpd_handle_t server   = NULL;                     // handle
    httpd_config_t config   = HTTPD_DEFAULT_CONFIG();   // 默认配置
    config.lru_purge_enable = true;                     // 清除少用链接

    // 启动 http 服务器
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // 设置 HTTP URI 对应的回调处理函数
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &status);
        httpd_register_uri_handler(server, &ctrl);
        return ESP_OK;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return ESP_FAIL;
}

void CJON_Test(httpd_req_t* req)
{
    cJSON* root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "message", "Hello, ESP32!");
    cJSON_AddStringToObject(root, "name", "小啵");
    cJSON_AddStringToObject(root, "name", "小泡");
    char* json_str = cJSON_Print(root);
    cJSON_Delete(root);

    httpd_resp_set_type(req, "application/json");
    httpd_resp_set_status(req, HTTPD_200);
    httpd_resp_send(req, json_str, strlen(json_str));
    free(json_str);
}

static void SCAN_header(httpd_req_t* req)
{
    char   buf[100];
    size_t buf_len;

    // 打印请求方法和URI
    ESP_LOGI(TAG, "HTTP Method: %s", http_method_str(req->method));
    ESP_LOGI(TAG, "URI: %s", req->uri);

    // 遍历并打印所有HTTP头字段
    httpd_req_get_hdr_value_len(req, (char*)&buf_len);
    if (buf_len > 0)
    {
        char* all_headers = malloc(buf_len + 1);
        if (all_headers)
        {
            httpd_req_get_hdr_value_str(req, NULL, all_headers, buf_len + 1);
            ESP_LOGI(TAG, "All Headers: %s", all_headers);
            free(all_headers);
        }
    }
}

esp_err_t http_request_handler(httpd_req_t* req)
{
    char buf[1024];
    char param[100];
    int  ret, remaining = req->content_len;

    // 打印请求方法、URI和协议版本
    ESP_LOGI(TAG, "HTTP Method: %s", http_method_str(req->method));
    ESP_LOGI(TAG, "URI: %s", req->uri);


    // 打印所有标头
    httpd_req_get_hdr_value_str(req, "Host", buf, sizeof(buf));
    ESP_LOGI(TAG, "Host: %s", buf);
    SCAN_header(req);
    char* query = strchr(req->uri, '?');
    if (query)
    {
        query++;   // 跳过'?'
        ESP_LOGI(TAG, "Query: %s", query);

        // 获取name参数
        if (httpd_query_key_value(query, "name", param, sizeof(param)) == ESP_OK)
        {
            ESP_LOGI(TAG, "name: %s", param);
        }
        else
        {
            ESP_LOGI(TAG, "name parameter not found");
        }
    }

    // 获取并打印自定义Header参数
    if (httpd_req_get_hdr_value_str(req, "status", buf, sizeof(buf)) == ESP_OK)
    {
        ESP_LOGI(TAG, "status: %s", buf);
    }
    else
    {
        ESP_LOGI(TAG, "status status not found");
    }

    while (remaining > 0)
    {
        if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
        {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT)
            {
                httpd_resp_send_408(req);
            }
            return ESP_FAIL;
        }
        ESP_LOGI(TAG, "%.*s", ret, buf);
        remaining -= ret;
    }

    // 打印完整请求正文（如果存在）
    if (req->content_len > 0)
    {
        char* content = malloc(req->content_len + 1);
        if (content)
        {
            ret          = httpd_req_recv(req, content, req->content_len);
            content[ret] = '\0';
            ESP_LOGI(TAG, "Content: %s", content);
            free(content);
        }
    }

    // 发送响应
    httpd_resp_send(req, "!!!!", HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

extern esp_err_t esp_mdns_discovery_start(void);
#include "wifi_devinit.h"
void mDNS_WebINit()
{
    WIFI_Init();
    esp_mdns_discovery_start();
    // esp_start_webserver();
    start_webserver(MOUNT_POINT);
}

/**********************************文件处理函数*************************************************************/

#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
#define SCRATCH_BUFSIZE (8192)
#define REST_CHECK(a, str, goto_tag, ...)                                         \
    do {                                                                          \
        if (!(a))                                                                 \
        {                                                                         \
            ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
            goto goto_tag;                                                        \
        }                                                                         \
    } while (0)

typedef struct rest_server_context
{
    char base_path[ESP_VFS_PATH_MAX + 1];
    char scratch[SCRATCH_BUFSIZE];
} rest_server_context_t;

#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
#define TEMP_STR_MAX_BYTE_SIZE (128)


static int httpdHexVal(char c)
{
    if (c >= '0' && c <= '9')
    {
        return c - '0';
    }

    if (c >= 'A' && c <= 'F')
    {
        return c - 'A' + 10;
    }

    if (c >= 'a' && c <= 'f')
    {
        return c - 'a' + 10;
    }

    return 0;
}

// Decode a percent-encoded value.
// Takes the valLen bytes stored in val, and converts it into at most retLen bytes that
// are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
// zero-terminates the ret buffer.
int httpdUrlDecode(char* val, int valLen, char* ret, int retLen)
{
    int s = 0, d = 0;
    int esced = 0, escVal = 0;

    while (s < valLen && d < retLen)
    {
        if (esced == 1)
        {
            escVal = httpdHexVal(val[s]) << 4;
            esced  = 2;
        }
        else if (esced == 2)
        {
            escVal += httpdHexVal(val[s]);
            ret[d++] = escVal;
            esced    = 0;
        }
        else if (val[s] == '%')
        {
            esced = 1;
        }
        else if (val[s] == '+')
        {
            ret[d++] = ' ';
        }
        else
        {
            ret[d++] = val[s];
        }

        s++;
    }

    if (d < retLen)
    {
        ret[d] = 0;
    }

    return d;
}

// Find a specific arg in a string(/update?output=4&state=1) of get- or post-data.
// Line is the string of post/get-data, arg is the name of the value to find. The
// zero-terminated result is written in buff, with at most buffLen bytes used. The
// function returns the length of the result, or -1 if the value wasn't found. The
// returned string will be urldecoded already.
int httpd_find_arg(const char* line, const char* arg, char* buff, int buffLen)
{
    char* e;
    char* p;
    bool  first_in = true;

    if (line == NULL)
    {
        return -1;
    }

    p = (char*)line;

    while (p != NULL && *p != '\n' && *p != '\r' && *p != 0)
    {
        // printf("findArg: %s\n", p);
        if (strncmp(p, arg, strlen(arg)) == 0 && p[strlen(arg)] == '=')
        {
            p += strlen(arg) + 1;   // move p to start of value
            e = (char*)strstr(p, "&");

            if (e == NULL)
            {
                e = p + strlen(p);
            }

            // printf("findArg: val %s len %d\n", p, (e-p));
            return httpdUrlDecode(p, (e - p), buff, buffLen);
        }

        if (first_in)
        {
            p        = (char*)strstr(p, "?");
            first_in = false;
        }
        else
        {
            p = (char*)strstr(p, "&");
        }

        if (p != NULL)
        {
            p += 1;
        }
    }

    printf("Finding %s in %s: Not found :/\n", arg, line);
    return -1;   // not found
}
/* Set HTTP response content type according to file extension */
static esp_err_t set_content_type_from_file(httpd_req_t* req, const char* filepath)
{
    const char* type = "text/plain";
    if (CHECK_FILE_EXTENSION(filepath, ".html"))
    {
        type = "text/html";
    }
    else if (CHECK_FILE_EXTENSION(filepath, ".js"))
    {
        type = "application/javascript";
    }
    else if (CHECK_FILE_EXTENSION(filepath, ".css"))
    {
        type = "text/css";
    }
    else if (CHECK_FILE_EXTENSION(filepath, ".png"))
    {
        type = "image/png";
    }
    else if (CHECK_FILE_EXTENSION(filepath, ".ico"))
    {
        type = "image/x-icon";
    }
    else if (CHECK_FILE_EXTENSION(filepath, ".svg"))
    {
        type = "text/xml";
    }
    return httpd_resp_set_type(req, type);
}

/* Send HTTP response with the contents of the requested file */
#include <stdio.h>
rest_server_context_t sendData = {0};
static esp_err_t rest_common_get_handler(httpd_req_t* req)
{
    char filepath[FILE_PATH_MAX];
    memset(filepath, 0, sizeof(filepath));
    // rest_server_context_t* rest_context = (rest_server_context_t*)req->user_ctx;
     rest_server_context_t* rest_context = &sendData;
      memset(rest_context, 0, sizeof(rest_context)); 
    printf(" req->uri %s\r\n", req->uri);

    // strlcpy(filepath, rest_context->base_path, sizeof(filepath));
    strlcat(filepath, "/sdcard", sizeof(filepath));
    if (req->uri[strlen(req->uri) - 1] == '/')
    {
        strlcat(filepath, "/index.html", sizeof(filepath));
    }
    else
    {
        strlcat(filepath, req->uri, sizeof(filepath));
    }

    char* p = strrchr(filepath, '?');
    if (p != NULL)
    {
        *p = '\0';
    }

    // int fd = open(filepath, O_RDONLY, 0);

    printf(" filepath %s\r\n ", filepath);

    FILE* file = fopen(filepath, "rb");   // "rb"用于二进制安全模式读取

    if (!file)
    {
        ESP_LOGE(TAG, "Failed to open file : %s", filepath);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
        return ESP_FAIL;
    }



    // if (fd == -1)
    // {
    //     ESP_LOGE(TAG, "Failed to open file : %s", filepath);
    //     /* Respond with 500 Internal Server Error */
    //     httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
    //     return ESP_FAIL;
    // }

    set_content_type_from_file(req, filepath);

    char*   chunk = rest_context->scratch;
    ssize_t read_bytes;
    do {
        /* Read file in chunks into the scratch buffer */
        // ESP_LOGI(TAG, "Free heap: %d bytes", heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
        // ESP_LOGI(TAG, "Minimum heap size: %d bytes", heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));

        // read_bytes = read(fd, chunk, SCRATCH_BUFSIZE);
         read_bytes = fread(chunk, 1, SCRATCH_BUFSIZE, file);;
        // ESP_LOGI(TAG, "Free heap: %d bytes", heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
        ESP_LOGI(TAG, "Minimum heap size: %d bytes", heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT));
        printf("read_bytes %d\r\n", read_bytes);
        // printf("%s\r\n", chunk);
        if (read_bytes == -1)
        {
            ESP_LOGE(TAG, "Failed to read file : %s", filepath);
        }
        else if (read_bytes > 0)
        {
            /* Send the buffer contents as HTTP response chunk */
            if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK)
            {
                // close(fd);
                 fclose(file);
                ESP_LOGE(TAG, "File sending failed!");
                /* Abort sending file */
                httpd_resp_sendstr_chunk(req, NULL);
                /* Respond with 500 Internal Server Error */
                httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
                return ESP_FAIL;
            }
        }
    } while (read_bytes > 0);
    /* Close file after sending complete */
    // close(fd);
     fclose(file);
    ESP_LOGI(TAG, "File sending complete");
    /* Respond with an empty chunk to signal HTTP response completion */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}
/***********************************************************************************************/




esp_err_t output_states_get_handler(httpd_req_t* req)
{
    esp_light_get_handler(req);
    return ESP_OK;
}
esp_err_t update_state_get_handler(httpd_req_t* req)
{
    return ESP_OK;
}
/* Send HTTP response with the contents of the requested file */
esp_err_t rest_common_get_handler(httpd_req_t* req);
esp_err_t start_webserver(const char* base_path)
{
    REST_CHECK(base_path, "wrong base path", err);
    rest_server_context_t* rest_context = calloc(1, sizeof(rest_server_context_t));
    REST_CHECK(rest_context, "No memory for rest context", err);
    strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path));

    httpd_handle_t server    = NULL;
    httpd_config_t config    = HTTPD_DEFAULT_CONFIG();
    config.max_uri_handlers  = 12;
    config.max_resp_headers  = 12;
    config.max_open_sockets  = 7;
    config.uri_match_fn      = httpd_uri_match_wildcard;
    config.send_wait_timeout = 10;
    config.stack_size        = 8192;
    ESP_LOGI(TAG, "Starting HTTP Server");
    REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start);

    httpd_uri_t httpd_uri_array[] = {
        {"/states", HTTP_GET, output_states_get_handler, rest_context},
        {"/update", HTTP_GET, update_state_get_handler, rest_context},
        {"/*", HTTP_GET, rest_common_get_handler, rest_context}
        // Catch-all callback function for the filesystem, this must be set to the array last one
    };

    for (int i = 0; i < sizeof(httpd_uri_array) / sizeof(httpd_uri_t); i++)
    {
        if (httpd_register_uri_handler(server, &httpd_uri_array[i]) != ESP_OK)
        {
            ESP_LOGE(TAG, "httpd register uri_array[%d] fail", i);
        }
    }

err_start:
    free(rest_context);
err:
    return ESP_FAIL;
}